#!/usr/bin/env node

/**
 * 上线前自动检查脚本
 * 自动验证系统各项配置和功能是否就绪
 */

const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const { promisify } = require('util');
const axios = require('axios');
const execAsync = promisify(exec);

// 加载环境配置
require('dotenv').config();

class PreLaunchChecker {
  constructor() {
    this.results = {
      passed: 0,
      failed: 0,
      warnings: 0,
      details: []
    };
    this.isProduction = process.env.NODE_ENV === 'production';
  }

  log(level, category, message, details = null) {
    const timestamp = new Date().toISOString();
    const logEntry = {
      timestamp,
      level,
      category,
      message,
      details
    };

    this.results.details.push(logEntry);

    const symbols = {
      'PASS': '✅',
      'FAIL': '❌',
      'WARN': '⚠️',
      'INFO': 'ℹ️'
    };

    console.log(`${symbols[level]} [${category}] ${message}`);
    if (details) {
      console.log(`   ${details}`);
    }

    if (level === 'PASS') this.results.passed++;
    if (level === 'FAIL') this.results.failed++;
    if (level === 'WARN') this.results.warnings++;
  }

  async checkFileExists(filePath, description) {
    try {
      await fs.promises.access(filePath);
      this.log('PASS', 'FILES', `${description} exists`, filePath);
      return true;
    } catch (error) {
      this.log('FAIL', 'FILES', `${description} missing`, filePath);
      return false;
    }
  }

  async checkEnvironmentFiles() {
    console.log('\n🔧 Checking Environment Configuration...');
    
    const envFiles = [
      { path: '.env', desc: 'Root environment file' },
      { path: '.env.local', desc: 'Local environment file' },
      { path: 'backend/.env.local', desc: 'Backend local environment' },
      { path: 'backend/.env.staging', desc: 'Backend staging environment' },
      { path: 'backend/.env.production', desc: 'Backend production environment' },
      { path: 'admin/.env.local', desc: 'Admin local environment' },
      { path: 'admin/.env.staging', desc: 'Admin staging environment' },
      { path: 'admin/.env.production', desc: 'Admin production environment' }
    ];

    for (const envFile of envFiles) {
      await this.checkFileExists(envFile.path, envFile.desc);
    }
  }

  async checkDatabaseFiles() {
    console.log('\n🗄️ Checking Database Configuration...');
    
    const dbFiles = [
      { path: 'backend/database/migrate.js', desc: 'Migration script' },
      { path: 'backend/database/backup.js', desc: 'Backup script' },
      { path: 'backend/database/db-manager.js', desc: 'Database manager' },
      { path: 'backend/database/init.sql', desc: 'Production database init' },
      { path: 'backend/database/init_local.sql', desc: 'Local database init' }
    ];

    for (const dbFile of dbFiles) {
      await this.checkFileExists(dbFile.path, dbFile.desc);
    }

    // 检查迁移目录
    try {
      const migrationsDir = 'backend/database/migrations';
      const migrations = await fs.promises.readdir(migrationsDir);
      this.log('INFO', 'DATABASE', `Found ${migrations.length} migration files`);
      
      if (migrations.length === 0) {
        this.log('WARN', 'DATABASE', 'No migration files found');
      }
    } catch (error) {
      this.log('FAIL', 'DATABASE', 'Migrations directory not found');
    }
  }

  async checkPackageFiles() {
    console.log('\n📦 Checking Package Configuration...');
    
    const packageFiles = [
      { path: 'package.json', desc: 'Root package.json' },
      { path: 'backend/package.json', desc: 'Backend package.json' },
      { path: 'admin/package.json', desc: 'Admin package.json' }
    ];

    for (const pkgFile of packageFiles) {
      if (await this.checkFileExists(pkgFile.path, pkgFile.desc)) {
        try {
          const content = await fs.promises.readFile(pkgFile.path, 'utf8');
          const pkg = JSON.parse(content);
          this.log('INFO', 'PACKAGES', `${pkg.name} v${pkg.version}`);
        } catch (error) {
          this.log('WARN', 'PACKAGES', `Invalid JSON in ${pkgFile.path}`);
        }
      }
    }
  }

  async checkNodeModules() {
    console.log('\n📚 Checking Dependencies...');
    
    const nodeModulesDirs = [
      'backend/node_modules',
      'admin/node_modules'
    ];

    for (const dir of nodeModulesDirs) {
      try {
        await fs.promises.access(dir);
        this.log('PASS', 'DEPS', `Dependencies installed: ${dir}`);
      } catch (error) {
        this.log('FAIL', 'DEPS', `Dependencies missing: ${dir}`);
      }
    }
  }

  async checkScripts() {
    console.log('\n📜 Checking Scripts...');
    
    const scripts = [
      { path: 'start-local-dev.sh', desc: 'Local development script' },
      { path: 'backend/database/migrate.js', desc: 'Migration script', executable: true },
      { path: 'backend/database/backup.js', desc: 'Backup script', executable: true },
      { path: 'backend/database/db-manager.js', desc: 'DB manager script', executable: true }
    ];

    for (const script of scripts) {
      if (await this.checkFileExists(script.path, script.desc)) {
        if (script.executable) {
          try {
            const stats = await fs.promises.stat(script.path);
            const isExecutable = (stats.mode & parseInt('111', 8)) !== 0;
            if (isExecutable) {
              this.log('PASS', 'SCRIPTS', `${script.desc} is executable`);
            } else {
              this.log('WARN', 'SCRIPTS', `${script.desc} is not executable`);
            }
          } catch (error) {
            this.log('WARN', 'SCRIPTS', `Cannot check permissions for ${script.path}`);
          }
        }
      }
    }
  }

  async checkDatabaseConnection() {
    console.log('\n🔌 Checking Database Connection...');
    
    try {
      // 检查本地SQLite数据库
      const localDbPath = process.env.DB_PATH || './backend/database/local.db';
      try {
        await fs.promises.access(localDbPath);
        this.log('PASS', 'DATABASE', 'Local SQLite database exists');
      } catch (error) {
        this.log('WARN', 'DATABASE', 'Local SQLite database not found');
      }

      // 尝试运行数据库健康检查
      try {
        const { stdout } = await execAsync('cd backend && npm run db:health');
        if (stdout.includes('Database connection successful')) {
          this.log('PASS', 'DATABASE', 'Database health check passed');
        } else {
          this.log('WARN', 'DATABASE', 'Database health check warnings');
        }
      } catch (error) {
        this.log('FAIL', 'DATABASE', 'Database health check failed', error.message);
      }
    } catch (error) {
      this.log('FAIL', 'DATABASE', 'Database connection check failed', error.message);
    }
  }

  async checkAPIEndpoints() {
    console.log('\n🌐 Checking API Endpoints...');
    
    const endpoints = [
      { url: 'http://localhost:3000/health', desc: 'Local API health' },
      { url: 'http://localhost:3001/health', desc: 'Staging API health' }
    ];

    if (this.isProduction) {
      endpoints.push(
        { url: 'https://api.cumrbull.com.sg/health', desc: 'Production API health' },
        { url: 'https://staging-api.cumrbull.com.sg/health', desc: 'Staging API health (HTTPS)' }
      );
    }

    for (const endpoint of endpoints) {
      try {
        const response = await axios.get(endpoint.url, { timeout: 5000 });
        if (response.status === 200) {
          this.log('PASS', 'API', `${endpoint.desc} responding`);
        } else {
          this.log('WARN', 'API', `${endpoint.desc} returned status ${response.status}`);
        }
      } catch (error) {
        this.log('FAIL', 'API', `${endpoint.desc} not responding`, error.message);
      }
    }
  }

  async checkSSLCertificates() {
    if (!this.isProduction) {
      console.log('\n🔒 Skipping SSL check (not production environment)');
      return;
    }

    console.log('\n🔒 Checking SSL Certificates...');
    
    const domains = [
      'api.cumrbull.com.sg',
      'admin.cumrbull.com.sg',
      'staging-api.cumrbull.com.sg'
    ];

    for (const domain of domains) {
      try {
        const response = await axios.get(`https://${domain}/health`, { 
          timeout: 10000,
          httpsAgent: new (require('https').Agent)({ rejectUnauthorized: true })
        });
        this.log('PASS', 'SSL', `${domain} SSL certificate valid`);
      } catch (error) {
        if (error.code === 'CERT_HAS_EXPIRED') {
          this.log('FAIL', 'SSL', `${domain} SSL certificate expired`);
        } else if (error.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
          this.log('FAIL', 'SSL', `${domain} SSL certificate invalid`);
        } else {
          this.log('WARN', 'SSL', `${domain} SSL check inconclusive`, error.message);
        }
      }
    }
  }

  async checkDNSResolution() {
    console.log('\n🌍 Checking DNS Resolution...');
    
    const domains = [
      { domain: 'cumrbull.com.sg', expectedIP: '185.230.63.171' },
      { domain: 'api.cumrbull.com.sg', expectedIP: '47.237.10.129' },
      { domain: 'admin.cumrbull.com.sg', expectedIP: '47.237.10.129' },
      { domain: 'staging-api.cumrbull.com.sg', expectedIP: '47.237.10.129' }
    ];

    for (const { domain, expectedIP } of domains) {
      try {
        const { stdout } = await execAsync(`nslookup ${domain}`);
        if (stdout.includes(expectedIP)) {
          this.log('PASS', 'DNS', `${domain} resolves to ${expectedIP}`);
        } else {
          this.log('WARN', 'DNS', `${domain} DNS resolution unexpected`);
        }
      } catch (error) {
        this.log('FAIL', 'DNS', `${domain} DNS resolution failed`, error.message);
      }
    }
  }

  async checkDocumentation() {
    console.log('\n📚 Checking Documentation...');
    
    const docs = [
      { path: 'README.md', desc: 'Project README' },
      { path: 'PRE_LAUNCH_CHECKLIST.md', desc: 'Pre-launch checklist' },
      { path: 'ENVIRONMENT_CONFIG_GUIDE.md', desc: 'Environment config guide' },
      { path: 'backend/database/DATABASE_ROLLBACK_GUIDE.md', desc: 'Database rollback guide' }
    ];

    for (const doc of docs) {
      await this.checkFileExists(doc.path, doc.desc);
    }
  }

  async generateReport() {
    console.log('\n📊 Generating Report...');
    
    const report = {
      timestamp: new Date().toISOString(),
      environment: process.env.NODE_ENV || 'development',
      summary: {
        total: this.results.passed + this.results.failed + this.results.warnings,
        passed: this.results.passed,
        failed: this.results.failed,
        warnings: this.results.warnings,
        success_rate: Math.round((this.results.passed / (this.results.passed + this.results.failed)) * 100)
      },
      details: this.results.details
    };

    // 保存报告到文件
    const reportPath = `pre-launch-report-${Date.now()}.json`;
    await fs.promises.writeFile(reportPath, JSON.stringify(report, null, 2));
    
    console.log('\n' + '='.repeat(60));
    console.log('📋 PRE-LAUNCH CHECK SUMMARY');
    console.log('='.repeat(60));
    console.log(`Environment: ${report.environment}`);
    console.log(`Total Checks: ${report.summary.total}`);
    console.log(`✅ Passed: ${report.summary.passed}`);
    console.log(`❌ Failed: ${report.summary.failed}`);
    console.log(`⚠️  Warnings: ${report.summary.warnings}`);
    console.log(`Success Rate: ${report.summary.success_rate}%`);
    console.log(`Report saved: ${reportPath}`);
    console.log('='.repeat(60));

    if (report.summary.failed > 0) {
      console.log('\n❌ CRITICAL ISSUES FOUND - DO NOT PROCEED WITH LAUNCH');
      console.log('Please resolve all failed checks before launching.');
      return false;
    } else if (report.summary.warnings > 0) {
      console.log('\n⚠️  WARNINGS DETECTED - REVIEW BEFORE LAUNCH');
      console.log('Consider addressing warnings before launching.');
      return true;
    } else {
      console.log('\n✅ ALL CHECKS PASSED - READY FOR LAUNCH');
      return true;
    }
  }

  async runAllChecks() {
    console.log('🚀 Starting Pre-Launch Checks...');
    console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
    console.log(`Timestamp: ${new Date().toISOString()}`);
    
    try {
      await this.checkEnvironmentFiles();
      await this.checkDatabaseFiles();
      await this.checkPackageFiles();
      await this.checkNodeModules();
      await this.checkScripts();
      await this.checkDatabaseConnection();
      await this.checkAPIEndpoints();
      await this.checkSSLCertificates();
      await this.checkDNSResolution();
      await this.checkDocumentation();
      
      return await this.generateReport();
    } catch (error) {
      console.error('\n❌ Pre-launch check failed:', error.message);
      return false;
    }
  }
}

// CLI 接口
async function main() {
  const checker = new PreLaunchChecker();
  const success = await checker.runAllChecks();
  process.exit(success ? 0 : 1);
}

if (require.main === module) {
  main();
}

module.exports = PreLaunchChecker;